package com.zyk.scaffold.tcp.client;

import com.zyk.scaffold.common.utils.MD5Util;
import com.zyk.scaffold.common.utils.RandomUtils;
import com.zyk.scaffold.common.utils.StringUtils;
import com.zyk.scaffold.tcp.proto3.MessageBuf;
import com.zyk.scaffold.tcp.utils.MsgUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.concurrent.CountDownLatch;

public class TcpClient {

    public static String host = "127.0.0.1";
    public static int port = 18889;
    public static volatile String token;

    public static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static Bootstrap bootstrap = getBootstrap();
    public static Channel channel = getChannel(host, port);

    /**
     * Init Bootstrap
     */
    public static final Bootstrap getBootstrap() {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group);
        b.channel(NioSocketChannel.class);
        b.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
                pipeline.addLast("decoder", new ProtobufDecoder(MessageBuf.JMTransfer.getDefaultInstance()));
                pipeline.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
                pipeline.addLast("encoder", new ProtobufEncoder());
                pipeline.addLast("handler", new ChannelInboundHandlerAdapter(){
                    public void channelRead(ChannelHandlerContext ctx, Object o) throws Exception {
                        MessageBuf.JMTransfer message = (MessageBuf.JMTransfer) o;
                        if(message == null){
                            System.out.println("message is null");
                            return;
                        }
                        System.out.println("Client Received Seq :" + message.getSeq());
                        System.out.println("Client Received Cmd :" + message.getCmd());
                        MessageBuf.JMTransfer.Body body = message.getBody();
                        if(body == null){
                            System.out.println("body is null");
                            return;
                        }
                        String msg = body.getMsg();
                        System.out.println("Client Received Msg :" + msg);
                        if(1111 == message.getSeq()){
                            token = msg;
                            countDownLatch.countDown();
                        }
                    }
                });
            }
        });


        b.option(ChannelOption.SO_KEEPALIVE, true);
        return b;
    }

    public static final Channel getChannel(String host, int port) {
        Channel channel;
        try {
            channel = bootstrap.connect(host, port).sync().channel();
        } catch (Exception e) {
            System.out.println("Connect Server (host[" + host + "]:port[" + port + "]) Failure." + e);
            return null;
        }
        return channel;
    }

    public static void connect(Object msg) throws Exception {
        if (channel != null) {
            channel.writeAndFlush(msg).sync();
        }
    }


    public static void pushMsg(String token, String msg) throws Exception {
        System.out.println("send:" + msg);
        TcpClient.connect(generatePushMsg(token, msg));
    }

    private static MessageBuf.JMTransfer generatePushMsg(String token, String msg) throws NoSuchAlgorithmException {
        String appId = "123";
        String timeStamp = LocalDateTime.now().toString();
        MessageBuf.JMTransfer.Builder builder = MessageBuf.JMTransfer.newBuilder();
        builder.setVersion("1.0");
        builder.setDeviceId("test");
        builder.setEncryptionTypeValue(1);
        builder.setPlatformTypeValue(1);
        builder.setName("1.0");
        builder.setGroupName("1.0");
        builder.setToken(token);
        builder.setAppId(appId);
        builder.setTimeStamp(timeStamp);
        builder.setSign("123");
        builder.setMsgType(MessageBuf.JMTransfer.MsgType.PUSH);
        builder.setCmd(1002);
        builder.setSeq(1234);
        builder.setBody(MessageBuf.JMTransfer.Body.newBuilder().setMsg(msg).build());
        builder.setSign(MD5Util.computeMD5(StringUtils.join(token, timeStamp, appId, msg)));
        return builder.build();
    }

    public static MessageBuf.JMTransfer.Builder generateHeartbeat() {
        MessageBuf.JMTransfer.Builder builder = MessageBuf.JMTransfer.newBuilder();
        builder.setVersion("1.0");
        builder.setDeviceId("test");
        builder.setEncryptionTypeValue(1);
        builder.setPlatformTypeValue(1);
        builder.setName("1.0");
        builder.setGroupName("1.0");
        builder.setToken("abc");
        builder.setAppId("123");
        builder.setTimeStamp(LocalDateTime.now().toString());
        builder.setSign("123");
        builder.setMsgType(MessageBuf.JMTransfer.MsgType.HEARTBEAT);
        builder.setCmd(1002);
        builder.setSeq(1234);
        builder.setBody(MessageBuf.JMTransfer.Body.newBuilder().setMsg(RandomUtils.getCharAndNumr(100)).build());
        return builder;
    }

    public static void heartbeat(long millis) throws Exception {
        try {
            for (; ; ) {
                System.out.println("heart beat");
                TcpClient.connect(generateHeartbeat());
                Thread.sleep(millis);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void oneHeartbeat() {
        try {
            System.out.println("oneHeartbeat");
            TcpClient.connect(generateHeartbeat().build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static int computeRawVarint32Size(final int value) {
        if ((value & (0xffffffff <<  7)) == 0) {
            return 1;
        }
        if ((value & (0xffffffff << 14)) == 0) {
            return 2;
        }
        if ((value & (0xffffffff << 21)) == 0) {
            return 3;
        }
        if ((value & (0xffffffff << 28)) == 0) {
            return 4;
        }
        return 5;
    }

    public static void writeRawVarint32(ByteBuf out, int value) {
        while (true) {
            if ((value & ~0x7F) == 0) {
                out.writeByte(value);
                return;
            } else {
                out.writeByte((value & 0x7F) | 0x80);
                value >>>= 7;
            }
        }
    }

    public static String login(String h_w) {
        try {
            System.out.println("login");
            TcpClient.connect(generateLogin().build());
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    private static MessageBuf.JMTransfer.Builder generateLogin() {
        MessageBuf.JMTransfer.Builder builder = MessageBuf.JMTransfer.newBuilder();
        builder.setVersion("1.0");
        builder.setDeviceId("test");
        builder.setEncryptionTypeValue(1);
        builder.setPlatformTypeValue(1);
        builder.setName("1.0");
        builder.setGroupName("1.0");
        builder.setToken("abc");
        builder.setAppId("123");
        builder.setTimeStamp(LocalDateTime.now().toString());
        builder.setSign("123");
        builder.setMsgType(MessageBuf.JMTransfer.MsgType.LOGIN);
        builder.setCmd(1111);
        builder.setSeq(1111);
        builder.setBody(MessageBuf.JMTransfer.Body.newBuilder().setMsg(RandomUtils.getCharAndNumr(100)).build());
        return builder;
    }
}
